/*
 * Conflict Detection Engine
 */
package rule;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author jmb
 */
public abstract class Detector {

    List<String> rulesPaths;
    public static final String OUTPUT_XML_FILE_PATH_SUFFIX = "output/detectionReport.pl";

    /**
     *
     * @param rulesPaths
     * @param anEng
     */
    public Detector(List<String> rulesPaths) {
        this.rulesPaths = rulesPaths;
    }

    /**
     * Runs the rules for the FI detection system
     * @return a String with the folder path containing the conflict results file
     */
    public abstract String detect();

    /**
     * Creates a string containing "current_predicate(rule_name,R#)," for
     * each of the rules in the rules files associated with the rulesPaths. The
     * Final line of the StringBuilder contains
     * Rules = [R1,R2, etc].
     * @param outputPath is the path tot he output folder (separated by '/')
     * @return the StringBuilder with the predicates and the rules.
     */
    public StringBuilder getcurrent_predicates(String outputPath) {
        StringBuilder data = new StringBuilder("% Autogenerated file listing all of the rules to test.\n");
        data.append("% ***** Feature rules to use during analysis  *****\n");
        data.append("getRules(Rules) :-\n");
        List<String> ruleNames = new ArrayList<String>();

        // Add all the rulenames from all of the files
        for (String s : rulesPaths) {
            //System.out.println(s);
            ruleNames.addAll(parseFile(s));
        }

        // Add all the current_predicate... entries
        int ruleNum = 0;
        for (String name : ruleNames) {
            data.append("current_predicate(" + name + ",R" + ruleNum++ + "),\n");
        }

        // Append the Rules = [...] values
        data.append("Rules = [");
        for (int i = 0; i < ruleNum; i++) {
            data.append("R" + i);
            if (i < ruleNum - 1) {
                data.append(",");
            }
        }
        data.append("].");
        data.append("\n\n% Consults the Prolog programs used in analysis\n");
        data.append("doConsults :-\n");
        data.append("consult('" + outputPath + "eventCalc'),\n");
        data.append("consult('" + outputPath + "conflict_rules'),\n");
        data.append("consult('" + outputPath + "utility'),\n");
        data.append("consult('" + outputPath + "detection'),\n");
        data.append("consult('" + outputPath + "outputFileWriter'),\n");
        String path = null;
        for (int i = 0; i < rulesPaths.size(); i++) {
            path = rulesPaths.get(i);
            if (i == rulesPaths.size() - 1) {
                data.append("consult('" + path.replace('\\', '/') + "').\n");
            } else {
                data.append("consult('" + path.replace('\\', '/') + "'),\n");
            }
        }
        data.append("\npath(X) :-\n\tX='" + outputPath +
                Detector.OUTPUT_XML_FILE_PATH_SUFFIX + "'.");
        return data;
    }

    /**
     * Writes data to an output file
     * @param data is the data to write
     * @param pathOut is the path to the output file
     */
    public void writeToFile(StringBuilder data, String pathOut) {
        DataOutputStream dos;
        try {
            File file = new File(pathOut);
            if (!file.exists()) {
                file.createNewFile();
            }
            dos = new DataOutputStream(new FileOutputStream(file, true));
            //System.out.println(data.toString());
            dos.writeChars(data.toString());
            dos.close();
        } catch (IOException e) {
            Logger.getLogger(this.getClass().getName()).
                    log(Level.SEVERE, null, e);
        }
    }

    /**
     * Opens a log file and generates an output file containing only the data points
     * @param path
     */
    public List<String> parseFile(String pathIn) {
        try {
            BufferedReader br = null;
            br = new BufferedReader(new FileReader(pathIn));
            String line;
            List<String> ruleNames = new ArrayList<String>();
            String temp = null;
            while ((line = br.readLine()) != null) {
                if (line.contains(":-")) {
                    temp = line.split("\\(")[0];
                    ruleNames.add(temp);
                    //System.out.println(temp);
                }
            }
            //System.out.println("ruleNames size:" + ruleNames.size());
            br.close();
            return ruleNames;
        } catch (IOException ex) {
            Logger.getLogger(Detector.class.getName()).log(Level.SEVERE, null, ex);
            return null;
        }
    }

    /**
     * From: http://www.java-tips.org/java-se-tips/java.io/how-to-copy-a-directory-from-one-location-to-another-loc.html
     * If targetLocation does not exist, it will be created.
     * @param sourceLocation
     * @param targetLocation
     * @throws IOException
     */
    public void copyDirectory(File sourceLocation, File targetLocation)
            throws IOException {

        if (sourceLocation.isDirectory()) {
            if (!targetLocation.exists()) {
                targetLocation.mkdir();
            }

            String[] children = sourceLocation.list();
            for (int i = 0; i < children.length; i++) {
                copyDirectory(new File(sourceLocation, children[i]),
                        new File(targetLocation, children[i]));
            }
        } else {

            InputStream in = new FileInputStream(sourceLocation);
            OutputStream out = new FileOutputStream(targetLocation);

            // Copy the bits from instream to outstream
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
        }
    }
}
